/*
 *
 * DOES NOT USE BUILT-IN DETECTION FUNCTION!!
alert tcp $EXTERNAL_NET $HTTP_PORTS -> $HOME_NET any (msg:"WEB-CLIENT Microsoft Excel extrst record arbitrary code excecution attempt"; flow:from_server,established; flowbits:isset,file.xls; content:"|fc 00|"; byte_test:1,&,4,12,relative; reference:url,technet.microsoft.com/en-us/security/bulletin/MS09-009; metadata:policy balanced-ips drop, policy security-ips drop, service http; reference:cve,2009-0238; classtype:attempted-user; sid:15365; rev:2;)
*/
/*
 * Use at your own risk.
 *
 * Copyright (C) 2005-2008 Sourcefire, Inc.
 * 
 * This file is autogenerated via rules2c, by Brian Caswell <bmc@sourcefire.com>
 */


#include "sf_snort_plugin_api.h"
#include "sf_snort_packet.h"

#include "so-util.h"

//#define DEBUG 1
#ifdef DEBUG
#define DEBUG_WRAP(code) code
#else
#define DEBUG_WRAP(code)
#endif
  
/* declare detection functions */
int rule15365eval(void *p);

/* declare rule data structures */
/* precompile the stuff that needs pre-compiled */
/* flow:established, from_server; */
static FlowFlags rule15365flow0 = 
{
    FLOW_ESTABLISHED|FLOW_TO_CLIENT
};

static RuleOption rule15365option0 =
{
    OPTION_TYPE_FLOWFLAGS,
    {
        &rule15365flow0
    }
};
/* flowbits:isset "file.xls"; */
static FlowBitsInfo rule15365flowbits1 =
{
    "file.xls",
    FLOWBIT_ISSET,
    0,
};

static RuleOption rule15365option1 =
{
    OPTION_TYPE_FLOWBIT,
    {
        &rule15365flowbits1
    }
};
// content:"|FC 00|", depth 0; 
static ContentInfo rule15365content2 = 
{
    (uint8_t *) "|FC 00|", /* pattern (now in snort content format) */
    0, /* depth */
    0, /* offset */
    CONTENT_BUF_NORMALIZED|CONTENT_RELATIVE, /* flags */
    NULL, /* holder for boyer/moore PTR */
    NULL, /* more holder info - byteform */
    0, /* byteform length */
    0 /* increment length*/
};

static RuleOption rule15365option2 = 
{
    OPTION_TYPE_CONTENT,
    {
        &rule15365content2
    }
};
/* byte_test:size 1, value 4, operator &, offset 12, relative; */
static ByteData rule15365byte_test3 = 
{
    1, /* size */
    CHECK_AND, /* operator */
    4, /* value */
    12, /* offset */
    0, /*multiplier */
    BYTE_BIG_ENDIAN|CONTENT_RELATIVE|CONTENT_BUF_NORMALIZED|EXTRACT_AS_BYTE /* flags */
};

static RuleOption rule15365option3 = 
{
    OPTION_TYPE_BYTE_TEST,
    {
        &rule15365byte_test3
    }
};

// content:"|85 00|", depth 0;
static ContentInfo rule15365content4 =
{
   (uint8_t *) "|85 00|", /* pattern (now in snort content format) */
   0, /* depth */
   0, /* offset */
   CONTENT_BUF_NORMALIZED, /* flags */
   NULL, /* holder for boyer/moore PTR */
   NULL, /* more holder info - byteform */
   0, /* byteform length */
   0 /* increment length*/
};

static RuleOption rule15365option4 =
{
   OPTION_TYPE_CONTENT,
   {
       &rule15365content4
   }
};

// file_data
static CursorInfo rule15365file_data5 =
{
    0, /* offset */
    CONTENT_BUF_NORMALIZED /* flags */
};

static RuleOption rule15365option5 =
{
#ifndef MISSINGFILEDATA
    OPTION_TYPE_FILE_DATA,
#else
    OPTION_TYPE_SET_CURSOR,
#endif
    {
        &rule15365file_data5
    }
};

/* references for sid 15365 */
/* reference: cve "2009-0238"; */
static RuleReference rule15365ref1 = 
{
    "cve", /* type */
    "2009-0238" /* value */
};

/* reference: url "technet.microsoft.com/en-us/security/bulletin/MS09-009"; */
static RuleReference rule15365ref2 = 
{
    "url", /* type */
    "technet.microsoft.com/en-us/security/bulletin/MS09-009" /* value */
};

static RuleReference *rule15365refs[] =
{
    &rule15365ref1,
    &rule15365ref2,
    NULL
};
/* metadata for sid 15365 */
/* metadata:service http, policy balanced-ips drop, policy security-ips drop; */
static RuleMetaData rule15365service1 = 
{
    "service http"
};

static RuleMetaData rule15365policy1 =
{
   "policy max-detect-ips drop"
};

static RuleMetaData *rule15365metadata[] =
{
    &rule15365service1,
    &rule15365policy1,
    NULL
};
RuleOption *rule15365options[] =
{
    &rule15365option0,
    &rule15365option1,
    &rule15365option2,
    &rule15365option3,
    &rule15365option4,
    &rule15365option5,
    NULL
};

Rule rule15365 = {
   
   /* rule header, akin to => tcp any any -> any any               */{
       IPPROTO_TCP, /* proto */
       "$EXTERNAL_NET", /* SRCIP     */
       "$HTTP_PORTS", /* SRCPORT   */
   
       0, /* DIRECTION */
       "$HOME_NET", /* DSTIP     */
   
       "any", /* DSTPORT   */
   },
   /* metadata */
   { 
       3,  /* genid (HARDCODED!!!) */
       15365, /* sigid */
       16, /* revision */
   
       "attempted-user", /* classification */
       0,  /* hardcoded priority XXX NOT PROVIDED BY GRAMMAR YET! */
       "FILE-OFFICE Microsoft Excel extrst record arbitrary code excecution attempt",     /* message */
       rule15365refs /* ptr to references */
       ,rule15365metadata
   },
   rule15365options, /* ptr to rule options */
   &rule15365eval, /* DO NOT use the built in detection function */
   0 /* am I initialized yet? */
};


/* detection functions */
int rule15365eval(void *p) {
    const uint8_t *cursor_normal = 0;
    SFSnortPacket *sp = (SFSnortPacket *) p;

    const uint8_t *beg_of_payload, *end_of_payload;
    const uint8_t *cursor_test, *cursor_temp;
    
    uint16_t char_size = 1; // if fHighByte bit set (DBCS String) 2 byte char, otherwise 1 byte ascii       
    uint16_t record_size;
    uint16_t count_char;    // count of characters in the string
    uint16_t count_run;     // count of formatting runs
    uint8_t  grbit;         // option flags


    DEBUG_WRAP(char name[]="rule15365eval");
    DEBUG_WRAP(printf("%s: enter\n", name));

    if(sp == NULL)
        return RULE_NOMATCH;

    if(sp->payload == NULL)
        return RULE_NOMATCH;

    // flow:established, from_server;
    if (checkFlow(p, rule15365options[0]->option_u.flowFlags) > 0 ) {
        // flowbits:isset "file.xls";
        if (processFlowbits(p, rule15365options[1]->option_u.flowBit) > 0) {
            #ifndef MISSINGFILEDATA
            if(fileData(p, rule15365options[5]->option_u.cursor, &cursor_normal) <= 0)
               return RULE_NOMATCH;
            #else
            if(setCursor(p, rule15365options[5]->option_u.cursor, &cursor_normal) <= 0)
               return RULE_NOMATCH;
            #endif
            // content:"|85 00|", depth 0; BoundSheet record (required, should be before SST)
            if(contentMatch(p, rule15365options[4]->option_u.content, &cursor_normal) <= 0)
               return RULE_NOMATCH;

            // Get the end of the payload for bounds checking.
            if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &beg_of_payload, &end_of_payload) <= 0)
               return RULE_NOMATCH;
            
            // content:"|FC 00|", depth 0; // record type
            while (contentMatch(p, rule15365options[2]->option_u.content, &cursor_normal) > 0)
            {
                DEBUG_WRAP(printf("%s |FC 00| found\n", name);)
                if(cursor_normal + 15 >= end_of_payload) 
                    return RULE_NOMATCH;

                cursor_test = cursor_normal;

                // record_size must be >= 11 becuase the size of metadata that SST has.
                // 11 = 4 byte cstTotal + 4 byte cstUnique + 2 byte cch + 1 byte grbit fields
                record_size = read_little_16(cursor_test);
                if (record_size < 11)
                    continue;                
                
                // byte_test:size 1, value 4, operator &, offset 12, relative;
                // if rExtSt bit set
                if (byteTest(p, rule15365options[3]->option_u.byte, cursor_test) > 0)
                {
                    cursor_test += 10;  
                    count_char  = read_little_16(cursor_test);
                    DEBUG_WRAP(printf("%s count_char(cch) : 0x%x\n", name, count_char);)

                    cursor_test += 2;
                    grbit = *cursor_test;
                    DEBUG_WRAP(printf("%s grbit : 0x%x\n", name, grbit);)

                    // make sure reserved bits are zero
                    if(grbit & 0xf2)
                       continue;
                      
                    cursor_test += 1;

                    if (grbit & 0x01) // if fHighByte bit set
                        char_size = 2;
                    
                    if (grbit & 0x08) // if fRishSt bit set
                    {
                        count_run = read_little_16(cursor_test);
                        
                        if (count_run > record_size)
                             return RULE_NOMATCH;

                        DEBUG_WRAP(printf("%s count_run(crun) : 0x%x\n", name, count_run);)

                        cursor_temp = cursor_test + 8 + (count_char * char_size) + (count_run * 4);                         
                        if (cursor_test < cursor_temp) // integer overflow check                        
                            cursor_test = cursor_temp; // 8 = 2 byte crun + 4 byte cbExtRst + 2 byte reserved in ExtRst record                        
                        else
                            continue;
                    }                    
                    else
                    {
                        cursor_temp = cursor_test + 6 + (count_char * char_size);
                        if (cursor_test < cursor_temp) // integer overflow check
                            cursor_test = cursor_temp; // 6 = 4 byte cbExtRst + 2 byte reserved in ExtRst record
                        else
                            continue;
                    }

                    if  (cursor_test < end_of_payload)
                    {
                        DEBUG_WRAP(printf("%s cb in ExtRst record : 0x%x\n", name, read_little_16(cursor_test));)
                        if (*cursor_test & 0x01) // if cb is an odd number. cb is in little endian
                            return RULE_MATCH;
                    }
                }
            }            
                                              
        }
    }
    return RULE_NOMATCH;
}

/*
Rule *rules[] = {
    &rule15365,
    NULL
};
*/
